package tool

import (
	"redis-check/client"
	"redis-check/common"
	"sync"
)

func RedisConnect(host client.RedisHost) *client.RedisClient {
	return RedisConnectDB(host, 0)
}

func RedisConnectDB(host client.RedisHost, db int32) *client.RedisClient {
	redisClient, err := client.NewRedisClient(host, db)
	if err != nil {
		panic(common.Logger.Errorf("创建Redis连接失败，地址：%v，DB：%v，异常信息：%v", host, db, err))
	}
	return &redisClient
}

func FetchRedisDbInfo(host client.RedisHost) (map[int32]int64, []string) {
	redisClient := RedisConnect(host)
	defer redisClient.Close()
	logicalDBMap, physicalDBList, err := redisClient.FetchBaseInfo(host.DBType == 1)
	if err != nil {
		panic(common.Logger.Critical(err))
	}
	common.Logger.Infof("源Redis类型：%v, DB列表：%v", host.DBType, physicalDBList)
	return logicalDBMap, physicalDBList
}

func FetchKeyTypes(keyInfo []*common.Key, client *client.RedisClient) {
	keyTypeStr, err := client.PipeTypeCommand(keyInfo)
	if err != nil {
		panic(common.Logger.Critical(err))
	}
	for i, t := range keyTypeStr {
		keyInfo[i].Tp = common.NewKeyType(t)
	}
}

func FetchKeyTypesIfNeed(keyInfo []*common.Key, client *client.RedisClient) {
	keyInfo = FilterKeys(keyInfo, func(key *common.Key) bool {
		return key.Tp == common.EndKeyType
	})
	if len(keyInfo) > 0 {
		FetchKeyTypes(keyInfo, client)
	}
}

func FetchKeyLens(keyInfo []*common.Key, client *client.RedisClient, source bool) {
	sourceKeyLen, err := client.PipeLenCommand(keyInfo)
	if err != nil {
		panic(common.Logger.Critical(err))
	}
	for i, keyLen := range sourceKeyLen {
		if source {
			keyInfo[i].SourceAttr.ItemCount = keyLen
		} else {
			keyInfo[i].TargetAttr.ItemCount = keyLen
		}
	}
}

func FetchKeyExists(keyInfo []*common.Key, client *client.RedisClient, source bool) {
	sourceKeyExists, err := client.PipeExistsCommand(keyInfo)
	if err != nil {
		panic(common.Logger.Critical(err))
	}
	for i, value := range sourceKeyExists {
		if source {
			keyInfo[i].SourceAttr.Exists = value == 1
		} else {
			keyInfo[i].TargetAttr.Exists = value == 1
		}
	}
}

func FetchTypeAndLen(keyInfo []*common.Key, sourceClient, targetClient *client.RedisClient) {
	FetchKeyTypes(keyInfo, sourceClient)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		FetchKeyLens(keyInfo, sourceClient, true)
		wg.Done()
	}()
	if targetClient != nil {
		wg.Add(1)
		go func() {
			FetchKeyLens(keyInfo, targetClient, false)
			wg.Done()
		}()
	}
	wg.Wait()
}

func FetchTypeAndLenIfNeed(keyInfo []*common.Key, sourceClient, targetClient *client.RedisClient) {
	// 对于没有类型的Key, 取类型和长度
	noTypeKeyInfo := FilterKeys(keyInfo, func(key *common.Key) bool {
		return key.Tp == common.EndKeyType
	})
	if len(noTypeKeyInfo) != 0 {
		FetchTypeAndLen(noTypeKeyInfo, sourceClient, targetClient)
	}
}

func FilterKeys(keyInfo []*common.Key, filter func(key *common.Key) bool) []*common.Key {
	if len(keyInfo) == 0 {
		return keyInfo
	}
	results := make([]*common.Key, 0, len(keyInfo))
	for i := 0; i < len(keyInfo); i++ {
		if filter(keyInfo[i]) {
			results = append(results, keyInfo[i])
		}
	}
	return results
}

func FetchKeyTTL(keyInfo []*common.Key, client *client.RedisClient, source bool) {
	keyExpire, err := client.PipeTTLCommand(keyInfo)
	if err != nil {
		panic(common.Logger.Critical(err))
	}
	for i, expire := range keyExpire {
		if source {
			keyInfo[i].SourceAttr.Expire = expire
		} else {
			keyInfo[i].TargetAttr.Expire = 0
		}
	}
}

func RecheckTTL(keyInfo []*common.Key, client *client.RedisClient) {
	reCheckKeys := make([]*common.Key, 0, len(keyInfo))
	for _, key := range keyInfo {
		if key.TargetAttr.ItemCount == 0 && key.SourceAttr.ItemCount > 0 {
			reCheckKeys = append(reCheckKeys, key)
		}
	}
	if len(reCheckKeys) != 0 {
		recheckTTL(reCheckKeys, client)
	}
}

func recheckTTL(keyInfo []*common.Key, client *client.RedisClient) {
	keyExpire, err := client.PipeTTLCommand(keyInfo)
	if err != nil {
		panic(common.Logger.Critical(err))
	}
	for i, expire := range keyExpire {
		if expire == 0 {
			keyInfo[i].SourceAttr.ItemCount = 0
		}
	}
}
